#pragma once

#include "SimpleCriticalSection.h"
#include "PortIndex.h"
#include "Binding.h"

namespace RoomFW {

class IActiveObject;
class Portal;

/// Provides registration of dynamic ports and actual dynamic binding services.
/// This class is not meant to be used outside the RoomFW.
/// For details, see http://cybertfs.msp.cyberoptics.com/sites/se500/Software%20Design%20Notes/Dynamic%20Port%20Binding.doc
///   @author Andrew Sage
class DynamicBindingManager
{
public:

	/// Get the single instance of this class.
	///   @return Reference to the single instance of this class.
	///        if createNew, delete the old one and create new.
	static DynamicBindingManager& Instance(bool createNew=false);

	/// Register a port for dynamic binding.
	///     @param activeObject - IActiveObject to register.
	///     @param port - Primary port index to register.
	///     @param dynamicPortName - Global name for above object/port pair.
	///     @param remote - True if this is a remote (interprocess) binding.
	///   @return Success.  Fails if name already registered.
	bool registerDynamicPort(IActiveObject* activeObject, PortIndex::PrimaryIndex port, const char* dynamicPortName, bool remote = false);

	/// Bind an object/port to a registered object port.
	///     @param activeObject - IActiveObject to bind.
	///     @param port - Primary port index to bind.
	///     @param dynamicPortName - Global name for object/port pair to bind to above pair.
	///   @return A dynamic binding shared pointer.  May be null if it fails.
	DynamicBindingSP dynamicBind(IActiveObject* activeObject, PortIndex::PrimaryIndex port, const char* dynamicPortName);

	/// Bind an portal to a registered object port.
	///     @param portal - Portal to bind.
	///     @param dynamicPortName - Global name for object/port pair to bind to above portal.
	///     @param contractTypeId - Expected contract type id.
	///     @param isConjugate - Expected conjugation of PORTAL after binding.
	///   @return A dynamic binding shared pointer.  May be null if it fails.
	DynamicBindingSP dynamicBind(Portal* portal, const char* dynamicPortName, ContractTypeId contractTypeId, bool isConjugate);

	/// Remove references to a binding.
	///     @param binding - Binding to remove.
	void unbind(BindingWrapperSP binding);

	/// Unregister all dynamic ports registerd for object.  Unbind any current dynamic binding to object.
	///     @param activeObject - IActiveObject to unregister.
	void unregister(IActiveObject* activeObject);

	/// Unbind any current dynamic binding to portal.
	///     @param portal - portal to unregister.
	void unregister(Portal* portal);

	//@{
	/// Methods for test case.
	bool hasRegistrations();
	bool hasBindings();
	//@}

private:

	/// Remove all bindings for object.
	void unbindObject(void* object);

	/// Synchronize all data access.
	SimpleCriticalSection dataSynch_;

	/// Registration info.  The boost multi-index container provides a container of structs with fast
	///	look-up on any struct attribute.  The namespaces and templates are hard to read, hence all
	///	the typedefs, but the usage is much simpler than the alternatives.
	struct Registration ///< Element type.
	{
		std::string portName;
		IActiveObject* object;
		PortIndex::PrimaryIndex port;
	};
	struct PortNameTag {};	///< Index tag
	struct ObjectTag {};	///< Index tag
	typedef boost::multi_index::multi_index_container<
		Registration,
		boost::multi_index::indexed_by<
			boost::multi_index::ordered_unique<boost::multi_index::tag<PortNameTag>, boost::multi_index::member<Registration, std::string, &Registration::portName> >,
			boost::multi_index::ordered_non_unique<boost::multi_index::tag<ObjectTag>, boost::multi_index::member<Registration, IActiveObject*, &Registration::object> >
		>
	> RegistrationSet;	///< Multi-index Container
	typedef RegistrationSet::iterator RegistrationSetIterator;
	typedef std::pair<RegistrationSetIterator, bool> RegistrationSetInsertReturn;
	typedef RegistrationSet::index<PortNameTag>::type RegistrationSetPortNameIndex;
	typedef RegistrationSetPortNameIndex::iterator RegistrationSetPortNameIterator;
	typedef std::pair<RegistrationSetPortNameIterator, bool> RegistrationSetPortNameInsertReturn;
	typedef RegistrationSet::index<ObjectTag>::type RegistrationSetObjectIndex;
	typedef RegistrationSetObjectIndex::iterator RegistrationSetObjectIndexIterator;
	typedef std::pair<RegistrationSetObjectIndexIterator, bool> RegistrationSetObjectInsertReturn;
	RegistrationSet registrations_;

	//@{ Binding info.
	/// Element type.
	struct BindingInfo
	{
		BindingWrapperSP binding;
		void* firstBound;
		void* secondBound;
	};
	struct FirstBoundTag {};	///<Index tag
	struct SecondBoundTag {};	///<Index tag
	struct BindingTag {};	///<Index tag
	typedef boost::multi_index::multi_index_container<
		BindingInfo,
		boost::multi_index::indexed_by<
			boost::multi_index::ordered_unique<boost::multi_index::tag<BindingTag>, boost::multi_index::member<BindingInfo, BindingWrapperSP, &BindingInfo::binding> >,
			boost::multi_index::ordered_non_unique<boost::multi_index::tag<FirstBoundTag>, boost::multi_index::member<BindingInfo, void*, &BindingInfo::firstBound> >,
			boost::multi_index::ordered_non_unique<boost::multi_index::tag<SecondBoundTag>, boost::multi_index::member<BindingInfo, void*, &BindingInfo::secondBound> >
		>
	> BindingInfoSet;	///< Multi-index Container
	typedef BindingInfoSet::iterator BindingInfoSetIterator;
	typedef std::pair<BindingInfoSetIterator, bool> BindingInfoSetInsertReturn;
	typedef BindingInfoSet::index<BindingTag>::type BindingInfoSetBindingIndex;
	typedef BindingInfoSetBindingIndex::iterator BindingInfoSetBindingIterator;
	typedef std::pair<BindingInfoSetBindingIterator, bool> BindingInfoSetBindingInsertReturn;
	typedef BindingInfoSet::index<FirstBoundTag>::type BindingInfoSetFirstBoundIndex;
	typedef BindingInfoSetFirstBoundIndex::iterator BindingInfoSetFirstBoundIndexIterator;
	typedef std::pair<BindingInfoSetFirstBoundIndexIterator, bool> BindingInfoSetFirstBoundInsertReturn;
	typedef BindingInfoSet::index<SecondBoundTag>::type BindingInfoSetSecondBoundIndex;
	typedef BindingInfoSetSecondBoundIndex::iterator BindingInfoSetSecondBoundIndexIterator;
	typedef std::pair<BindingInfoSetSecondBoundIndexIterator, bool> BindingInfoSetSecondBoundInsertReturn;
	BindingInfoSet bindings_;
	//@}

	/// Make this class a singleton.
	DynamicBindingManager() {}
};

}
